//    Copyright (C) Kherty.  All rights reserved.

using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Spreadsheet.Functions;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{

    internal sealed class ExternNameRecord : WorkbookLevelRecord
    {
        public ExternNameRecord(Function function, ushort index)
        {
            _name = function.Name;
            _index = index;
            body = new AddinUdf(function.Name);
        }


        private ushort _index;
        private readonly string _name;

#if DEBUG
        public string Name
        {
            get { return _name; }
        }
#endif


        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.ExternName; }
        }

        internal override void WriteContent(BiffWriterWorkbookContext context)
        {
            context.WriteUInt16(Flags);
            body.Write(context.Writer);
        }

        private ExternNameStructure body;
        private ExternalBookContext externBookContext;
        private bool fBuiltIn;
        private bool fWantAdvise;
        private bool fOle;
        private bool fWantPict;
        private bool fOleLink;
        private ushort cf;
        private bool fIcon;

        public ExternNameRecord()
        {
        }

        protected override void ReadContent(BiffReaderContext context, int length)
        {
#if DEBUG
            long position = context.BaseStream.Position;
#endif
            ushort flags = context.ReadUInt16();
            fBuiltIn = (flags & 0x01) != 0;
            fWantAdvise = (flags & 0x02) != 0;
            fWantPict = (flags & 0x04) != 0;
            fOle = (flags & 0x08) != 0;
            fOleLink = (flags & 0x10) != 0;
            cf = (ushort) ((flags >> 5) & 0x3FF);
            fIcon = (flags & (0x8000)) != 0;
            Contract.Assert(flags == Flags);
            SupBookRecord previous = context.PreviousSupBook;
            Contract.Assert(previous != null);
            if (previous.IsAddInReferencing)
            {
                body = new AddinUdf();
                body.Read(context.Reader);
                return;
            }
            if (fOle)
            {
                var externDdeLinkNoOper = new ExternDdeLinkNoOper();
                externDdeLinkNoOper.Read(context.Reader);
                body = externDdeLinkNoOper;
            }
            else
            {
                if (fOleLink)
                    throw new NotImplementedException();
                else
                {
                    var p = new VirtualPath(previous.VirtualPath);
                    Debug.WriteLine(previous.VirtualPath + " " + p.IsOle);
                    if (p.IsOle)
                    {
                        var externOleDdeLink = new ExternOleDdeLink();
                        externOleDdeLink.Read(context.Reader, length);
                        body = externOleDdeLink;
                    }
                    else
                    {
                        var ExternDocName = new ExternDocName();
                        ExternDocName.Read(context.Reader);
                        body = ExternDocName;
                    }
                }
            }
#if DEBUG
            Contract.Assert(context.BaseStream.Position == position + length);
#endif
        }

        private class VirtualPath
        {
            private readonly string _value;

            public VirtualPath(string s)
            {
                _value = s;
            }

            public bool IsOle
            {
                get
                {
                    State state = State.Initial;
                    
                    foreach (char c in _value)
                    {
                        if (IsPathCharacter(c))
                        {
                            switch (state)
                            {
                                case State.Initial:
                                    state = State.FirstPath;
                                    break;
                                case State.FirstPath:
                                    case State.SecondPath:
                                    continue;
                                    case State.Separator:
                                    state = State.SecondPath;
                                    break;
                            }
                            continue;
                        }
                        if (c == 0x0003)
                        {
                            switch (state)
                            {
                                    case State.FirstPath:
                                    state = State.Separator;
                                    break;
                                default:
                                    return false;
                            }
                        }
                        else
                        {
                            return false;
                        }
                    }
                    return state == State.SecondPath;
                }
            }
            enum State
            {
                Initial,
                FirstPath,
                Separator,
                SecondPath
            }

            private static bool IsPathCharacter(char c)
            {
                int i = c;
                if (i >= 0x0020 && i <= 0x0021)
                    return true;
                if (i >= 0x0023 && i <= 0x0029)
                    return true;
                if (i >= 0x002B && i <= 0x002E)
                    return true;
                if (i >= 0x0030 && i <= 0x0039)
                    return true;
                if (i == 0x003A)
                    return true;
                if (i == 0x003B)
                    return true;
                if (i == 0x003D)
                    return true;
                if (i >= 0x0040 & i <= 0x005B)
                    return true;
                if (i >= 0x005D && i <= 0x007B)
                    return true;
                if (i
                    >= 0x007D && i <= 0xFFFF)
                    return true;
                return false;
            }
        }

        protected ushort Flags
        {
            get
            {
                ushort result = 0;
                if (fBuiltIn)
                    result |= 0x0001;
                if (fWantAdvise)
                    result |= 0x0002;
                if (fWantPict)
                    result |= 0x0004;
                if (fOle)
                    result |= 0x0008;
                if (fOleLink)
                    result |= 0x0010;
                result |= (ushort) (cf << 5);
                if (fIcon)
                    result |= 0x8000;
                return result;
            }
        }


        public override void Apply(BiffReaderWorkbookContext context)
        {
            externBookContext = context.ExternalBookContexts[context.ExternalBookContexts.Count - 1];


            context.GetActions(ActionType.ResolveExternalName).Add(delegate
                                                                       {
                                                                           externBookContext.DefinedNames.Add(body.Name);
/*                                                                           if (body is AddinUdf)
                                                                           {
                                                                               AddinUdf addinUdf = (AddinUdf) body;
                                                                               this.externBookContext.DefinedNames.Add(
                                                                                   (string) addinUdf.udfName);
                                                                           }
                                                                           else if (body is ExternDocName)
                                                                           {
                                                                               ExternDocName externDocName = (ExternDocName) body;
                                                                               this.externBookContext.DefinedNames.Add(
                                                                                   (string)externDocName.Name);
                                                                           }
                                                                           else if (body is ExternOleDdeLink)
                                                                           {
                                                                               ExternOleDdeLink externOleDdeLink = (ExternOleDdeLink) body;
                                                                               this.externBookContext.DefinedNames.Add(externOleDdeLink.LinkName);
                                                                           }
                                                                           else
                                                                           {
                                                                               throw new NotImplementedException();
                                                                           }*/
                                                                       });
        }

        #region Nested type: ExterNameType

        #endregion
    }

    internal class ExternDdeLinkNoOper : ExternNameStructure
    {
        private ShortXLUnicodeString linkName;
        private uint reserved;

        public override string Name
        {
            get { return (string) linkName; }
        }

        public override void Read(BinaryReader reader)
        {
            reserved = reader.ReadUInt32();
            Contract.Assert(reserved == 0);
            linkName = new ShortXLUnicodeString();
            linkName.Read(reader);
            Contract.Assert((string) linkName == "StdDocumentName");
        }

        public override void Write(BinaryWriter writer)
        {
            writer.Write((uint) 0);
            linkName.Write(writer);
        }
    }
}